package com.goozao.vc.project.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.logging.log4j.LogManager;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.CellValue;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.goozao.core.exception.ServiceException;
import com.goozao.core.util.ExcelUtil;
import com.goozao.core.util.GoozaoLoggerUtil;
import com.goozao.dymatic.GModel;
import com.goozao.dymatic.config.domain.ConfigAttribute;
import com.goozao.dymatic.config.domain.ConfigStatus;
import com.goozao.dymatic.config.domain.ConfigType;
import com.goozao.dymatic.config.service.IConfigTypeService;
import com.goozao.dymatic.eobject.api.EObjectApi;
import com.goozao.dymatic.eobject.domain.EObject;
import com.goozao.dymatic.openapi.config.IConfigAttributeManager;
import com.goozao.dymatic.openapi.system.ISysDeptManager;
import com.goozao.dymatic.openapi.system.ISysDictManager;
import com.goozao.dymatic.openapi.system.ISysUserManager;
import com.goozao.project.domain.ScheduleTask;
import com.goozao.project.domain.TeamMember;
import com.goozao.project.service.IScheduleTaskService;
import com.goozao.project.service.ITeamMemberService;
import com.goozao.vc.project.IProjectService;
import com.goozao.vc.project.TaskInfo;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.common.utils.StringUtils;

@Service
public class ProjectServiceImpl implements IProjectService {

	private static final org.apache.logging.log4j.Logger syslog = LogManager.getLogger(ProjectServiceImpl.class);
	private Logger logger = null;
	
	private static final String TEMPLATE_SCHEDULE_TASK = "/project/ScheduleTask.xlsx"; //导出计划模版
	
	@Autowired
	private EObjectApi api;

	@Autowired
	private IConfigTypeService configTypeService;

	@Autowired
	private ISysDictManager dictManager;

	@Autowired
	private IConfigAttributeManager attributeManager;

	@Autowired
	private ITeamMemberService teamMemberService;

	@Autowired
	private ISysUserManager userManager;

	@Autowired
	private ISysDeptManager deptManager;
	
	@Autowired
	private IScheduleTaskService scheduleTaskService;

	@Override
	public String importSchedule(String scheduleOid, String originalFilename, InputStream inputStream)
			throws Exception {
		// 每次执行生成一个新的日志
		logger = GoozaoLoggerUtil.create(ProjectServiceImpl.class.getName(),
				RuoYiConfig.getLogRootFolder() + "/ImportSchedule/" + DateUtils.getDate() + "/ImportScheduleTask_"
						+ ShiroUtils.getLoginName() + "_" + DateUtils.dateTimeNow() + ".log");
		if (logger == null) {
			throw new ServiceException("创建日志失败");
		}

		try {
			// 获取计划
			EObject schedule = this.api.getByOid(scheduleOid);
			if (schedule == null) {
				return "没有找到计划!";
			}

			// 获取所属项目
			String projectOid = this.api.getAttributeValue(schedule, "projectId");
			if (StringUtils.isEmpty(projectOid)) {
				return "计划没有设定项目!";
			}
			EObject project = this.api.getByOid(projectOid);
			if (project == null) {
				return "计划选择的所属项目不正确，请先选择所属项目再导入计划。";
			}

			ConfigType taskConfigType = this.api.getConfigTypeByKey(ScheduleTask.TYPE_SCHEDULE_TASK);
			ConfigStatus workConfigStatus = this.api.getStatusByKey(ConfigStatus.STATUS_KEY_WORKING);

			// 读取并验证表格内容
			List<TaskInfo> taskList = readAndValidExcel(originalFilename, inputStream);

			// 查询项目成员
			List<TeamMember> teamMemberList = teamMemberService.getAllProjectTeamMember(project);

			Map<String, String> deptAndUserMap = new HashMap<String, String>();

			StringBuilder validErr = new StringBuilder();

			// 验证每个责任部门都有项目成员
			// 读出所有责任部门
//			List<String> allRespDept = new ArrayList<String>();
//			for (TaskInfo taskInfo : taskList) {
//				String respDept = taskInfo.getRespDept();
//				if (allRespDept.contains(respDept)) {
//					continue;
//				}
//				allRespDept.add(respDept);
//				logger.info(StringUtils.format("涉及到责任部门:{}", respDept));
//			}
			// validRespDept(allRespDept, teamMemberList, deptAndUserMap, validErr);

			// 验证每个责任人都是项目成员
			// 读出所有责任人
			List<String> allOwnerUser = new ArrayList<String>();
			for (TaskInfo taskInfo : taskList) {
				String ownerUserId = taskInfo.getOwnerUserId();
				if (allOwnerUser.contains(ownerUserId)) {
					continue;
				}
				allOwnerUser.add(ownerUserId);
				logger.info(StringUtils.format("涉及到责任人:{}", ownerUserId));
			}
			validOwnerUser(allOwnerUser, teamMemberList, validErr);

			if (StringUtils.isNotEmpty(validErr.toString())) {
//				return StringUtils.format("导入计划的责任部门在当前项目成员中找不到对应的子经理，请检查:<br>{}", validErr.toString());
				return StringUtils.format("导入计划的责任人在当前项目成员中不存在，请检查:<br>{}", validErr.toString());
			}

			// 导入任务
			logger.info(StringUtils.format("共计[{}]个任务需要导入", taskList.size()));
			int i = 0;
			boolean hasErr = false;
			StringBuilder errMsg = new StringBuilder();

			ConfigAttribute phaseConfig = attributeManager.get(taskConfigType.getOid(), ScheduleTask.ATTR_PHASE);
			ConfigAttribute deptConfig = attributeManager.get(taskConfigType.getOid(), ScheduleTask.ATTR_TASK_RESPDEPT);
			ConfigAttribute programConfig = attributeManager.get(taskConfigType.getOid(), "workProgram");
			ConfigAttribute detailConfig = attributeManager.get(taskConfigType.getOid(), "workDetails");

			for (TaskInfo task : taskList) {
				logger.info(StringUtils.format("准备导入任务[{}]:{}", (i + 1), task.toString()));
				try {

					EObject newScheduleTask = importTask(task, scheduleOid, projectOid, taskConfigType,
							workConfigStatus, phaseConfig, deptConfig, programConfig, detailConfig, deptAndUserMap);
					logger.info(StringUtils.format("创建任务[{}]成功:{}", (i + 1),
							configTypeService.getSimpleName(newScheduleTask)));
				} catch (Exception e) {
					logger.log(Level.SEVERE, GoozaoLoggerUtil.getExceptionMsg(e));
					syslog.error("{}", e);
					hasErr = true;
					errMsg.append("创建任务[" + (i + 1) + "]失败:").append(e.getMessage()).append("<br>");
				}
				i++;
			}

			if (hasErr) {
				return StringUtils.format("导入存在失败<br>{}", errMsg.toString());
			}

			return "导入成功";
		} catch (Exception e) {
			logger.log(Level.SEVERE, GoozaoLoggerUtil.getExceptionMsg(e));
			syslog.error("", e);
			return GoozaoLoggerUtil.getExceptionMsg(e);
		} finally {
			GoozaoLoggerUtil.closeLog(logger);
		}
	}

	/**
	 * 验证每个责任部门都有项目成员
	 * 
	 * @deprecated
	 */
	private void validRespDept(List<String> allRespDept, List<TeamMember> teamMemberList,
			Map<String, String> deptAndUserMap, StringBuilder validErr) {
		for (String targetRespDept : allRespDept) {
			logger.info(StringUtils.format("验证责任部门:{}", targetRespDept));
			if ("项目经理".equals(targetRespDept) || "项目质量监督".equals(targetRespDept)) {
				logger.info("项目经理/项目质量监督，即本人，无需验证!");
				continue;
			}
			String[] deptArr = targetRespDept.split("-");
			boolean findout = false;
			for (TeamMember tm : teamMemberList) {
				String userRole = tm.getProjectRole();
				if (!"project_manager_level2".equals(userRole)) {
					logger.info(StringUtils.format("当前成员[{}]的项目角色是[{}]，非子经理，不予考虑", tm.toString(), userRole));
					continue;
				}
				String userId = tm.getUserId();
				SysUser user = userManager.getByLoginName(userId);
				if (user == null) {
					continue;
				}

				SysDept dept = user.getDept();
				if (dept != null) {
					String currentDeptName = dept.getDeptName();
					SysDept parentDept = deptManager.selectDeptById(dept.getParentId());
					String parentDeptName = null;
					if (parentDept != null) {
						parentDeptName = parentDept.getDeptName();
					}
					logger.info(StringUtils.format("用户[{}]的组织全称:{}-{}", userId, parentDept, currentDeptName));

					if (deptArr.length == 1) {
						if (currentDeptName.equals(targetRespDept)) {
							findout = true;
							deptAndUserMap.put(targetRespDept, userId);
							logger.info(StringUtils.format("找到责任部门[{}]的子经理[{}]", targetRespDept, userId));
							break;
						}
					} else if (deptArr.length == 2) {
						String fullDeptName = parentDeptName + "-" + currentDeptName;
						if (fullDeptName.equals(targetRespDept)) {
							findout = true;
							deptAndUserMap.put(targetRespDept, userId);
							logger.info(StringUtils.format("找到责任部门[{}]的子经理[{}]", targetRespDept, userId));
							break;
						}
					}
				}
			}

			if (!findout) {
				validErr.append(targetRespDept).append("<br>");
			}
		}
	}

	/**
	 * 验证每个责任人都是项目成员
	 */
	private void validOwnerUser(List<String> allOwnerUser, List<TeamMember> teamMemberList, StringBuilder validErr) {
		for (String targetOwnerUser : allOwnerUser) {
			logger.info(StringUtils.format("验证责任人:{}", targetOwnerUser));
			boolean findout = false;
			for (TeamMember tm : teamMemberList) {
				String userId = tm.getUserId();
				if (targetOwnerUser.equals(userId)) {
					findout = true;
					break;
				}
			}

			if (!findout) {
				validErr.append(targetOwnerUser).append("<br>");
			}
		}
	}

	private EObject importTask(TaskInfo task, String scheduleOid, String projectOid, ConfigType taskConfigType,
			ConfigStatus workConfigStatus, ConfigAttribute phaseConfig, ConfigAttribute deptConfig,
			ConfigAttribute programConfig, ConfigAttribute detailConfig, Map<String, String> deptAndUserMap)
			throws Exception {
		EObject scheduleTask = new EObject();
		String newOid = UUID.randomUUID().toString();
		this.api.setAttributeValue(scheduleTask, GModel.ATTR_OID, newOid);
		this.api.setAttributeValue(scheduleTask, GModel.ATTR_OTYPE, taskConfigType.getOid());
		this.api.setAttributeValue(scheduleTask, GModel.ATTR_ONAME, task.getTaskName());
		this.api.setAttributeValue(scheduleTask, GModel.ATTR_REV_ID,
				configTypeService.nextRev(taskConfigType.getOid(), null));
		this.api.setAttributeValue(scheduleTask, GModel.ATTR_OSTATUS, workConfigStatus.getOid());
		this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_OWNER_PROJECT, projectOid);
		this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_SCHEDULE, scheduleOid);

		this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_TASK_IS_RELEASED, "N"); // 计划已发布
		this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_TASK_STATUS, ScheduleTask.TASK_STATUS_UNDEFINED); // 任务状态

		String phaseValue = dictManager.getValue(phaseConfig.getDict(), task.getPhase());
		this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_PHASE, phaseValue); // 项目阶段

		String workProgram = dictManager.getValue(programConfig.getDict(), task.getWorkProgram());
		this.api.setAttributeValue(scheduleTask, "workProgram", workProgram); // 工作程序

		String workDetails = dictManager.getValue(detailConfig.getDict(), task.getWorkDetail());
		this.api.setAttributeValue(scheduleTask, "workDetails", workDetails); // 工作明细

		String targetDespDept = task.getRespDept();
		String deptValue = dictManager.getValue(deptConfig.getDict(), targetDespDept);
		this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_TASK_RESPDEPT, deptValue); // 责任部门
		
		this.api.setAttributeValue(scheduleTask, GModel.ATTR_OWNERUSER, task.getOwnerUserId()); // 责任人

		if ("项目质量监督".equals(targetDespDept)) {
			this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_IS_MILESTONE, "Y"); // 里程碑
		}

		this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_PLAN_STARTDATE, task.getPlanStartDate()); // 计划开始时间
		this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_PLAN_ENDDATE, task.getPlanEndDate()); // 计划结束时间
		this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_PROGRESS, "0"); // 已完成%
		this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_DUARATION, task.getWorkDays()); // 持续天数(d)

//		if (!"项目经理".equals(targetDespDept)) {
//			String targetUserId = deptAndUserMap.get(targetDespDept);
//			this.api.setAttributeValue(scheduleTask, GModel.ATTR_OWNERUSER, targetUserId);
//			logger.info(StringUtils.format("任务[{}]的责任部门[{}]，所有者:{}", scheduleTask.getOname(), targetDespDept,
//					targetUserId));
//		} else {
//			logger.info(StringUtils.format("任务[{}]的责任部门[项目经理]，所有者为当前用户，即项目经理", scheduleTask.getOname()));
//		}

		return this.api.create(scheduleTask);
	}

	private List<TaskInfo> readAndValidExcel(String originalFilename, InputStream inputStream) throws IOException {

		Workbook workbook = WorkbookFactory.create(inputStream);
		if (workbook == null) {
			throw new IOException("无法解析到Excel的工作簿!");
		}
		Sheet sheet = workbook.getSheetAt(0);
		if (sheet == null) {
			throw new IOException("无法解析到Excel的第一个Sheet!");
		}
		try {

			// 创建公式求值器
			FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
			StringBuilder err = new StringBuilder(); // 全部校验一遍基本信息
			List<TaskInfo> list = new ArrayList<>();
			int titleNum = 1;
			int rows = sheet.getLastRowNum();
			for (int i = titleNum; i <= rows; i++) {
				Row row = sheet.getRow(i);
				int rowIndex = i + 1;

				int col = 0; // 注意以下读取顺序必须与EXCEL列顺序一致
				String phase = ExcelUtil.getCellValue(row, col++); // 项目阶段
				if (StringUtils.isEmpty(phase)) {
					logger.log(Level.WARNING, "第[" + rowIndex + "]行的项目阶段为空，不再继续读取数据！");
					break;
				}
				String workProgram = ExcelUtil.getCellValue(row, col++); // 工作程序
				String workDetail = ExcelUtil.getCellValue(row, col++); // 工作明细
				String taskName = ExcelUtil.getCellValue(row, col++); // 任务名称/交付物
				String respDept = ExcelUtil.getCellValue(row, col++); // 责任部门
				String ownerUserId = ExcelUtil.getCellValue(row, col++); // 责任人

				String planStartDate = ""; // 开始时间
				int startCol = col++;
				Cell cell1 = row.getCell(startCol);
				if (cell1 != null && cell1.getCellType() == CellType.FORMULA) {
					CellValue cellValue = evaluator.evaluate(cell1);
					if (CellType.NUMERIC.equals(cellValue.getCellType())) {
						Date date = cell1.getDateCellValue();
						SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
						planStartDate = sdf.format(date);
					}
				}
				if (StringUtils.isEmpty(planStartDate)) {
					planStartDate = ExcelUtil.getCellValue(row, startCol);
				}

				String planEndDate = ""; // 结束时间
				int endCol = col++;
				Cell cell2 = row.getCell(endCol);
				if (cell2 != null && cell2.getCellType() == CellType.FORMULA) {
					CellValue cellValue = evaluator.evaluate(cell2);
					if (CellType.NUMERIC.equals(cellValue.getCellType())) {
						Date date = cell2.getDateCellValue();
						SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
						planEndDate = sdf.format(date);
					}
				}
				if (StringUtils.isEmpty(planEndDate)) {
					planEndDate = ExcelUtil.getCellValue(row, endCol);
				}

				String workDays = ""; // 工作天数
				int dayCol = col++;
				Cell cellDay = row.getCell(dayCol);
				if (cellDay != null && cellDay.getCellType() == CellType.FORMULA) {
					CellValue cellValue = evaluator.evaluate(cellDay);
					if (CellType.NUMERIC.equals(cellValue.getCellType())) {
						workDays = Convert.toInt(cellValue.getNumberValue(), 0) + "";
					}
				}
				if (StringUtils.isEmpty(workDays) || "0".equals(workDays)) {
					workDays = ExcelUtil.getCellValue(row, dayCol);
				}

				TaskInfo task = new TaskInfo();
				task.setPhase(phase);
				task.setWorkProgram(workProgram);
				task.setWorkDetail(workDetail);
				task.setTaskName(taskName);
				task.setRespDept(respDept);
				task.setOwnerUserId(ownerUserId);
				task.setPlanStartDate(planStartDate);
				task.setPlanEndDate(planEndDate);
				task.setWorkDays(workDays);
				list.add(task);
				logger.info(StringUtils.format("读取第[{}]行:{}", rowIndex, task.toString()));

				if (StringUtils.isEmpty(phase) || StringUtils.isEmpty(taskName) || StringUtils.isEmpty(respDept)
						|| StringUtils.isEmpty(ownerUserId) || StringUtils.isEmpty(planStartDate)
						|| StringUtils.isEmpty(planEndDate) || StringUtils.isEmpty(workDays)) {
					err.append(StringUtils.format("第[{}]行内容有误，项目阶段、任务名称、责任部门、责任人、开始时间、结束时间、工作天数不可为空!<br>", rowIndex));
				}

			}
			if (StringUtils.isNotEmpty(err)) {
				throw new ServiceException("校验失败，请检查数据<br>" + err.toString());
			}
			return list;
		} finally {
			if (workbook != null) {
				workbook.close();
			}
		}
	}
	

	@Override
	public String importSchedule2(String scheduleOid, String originalFilename, InputStream inputStream) {
		// 每次执行生成一个新的日志
				logger = GoozaoLoggerUtil.create(ProjectServiceImpl.class.getName(),
						RuoYiConfig.getLogRootFolder() + "/ImportSchedule/" + DateUtils.getDate() + "/ImportScheduleTask_"
								+ ShiroUtils.getLoginName() + "_" + DateUtils.dateTimeNow() + ".log");
				if (logger == null) {
					throw new ServiceException("创建日志失败");
				}

				try {
					// 获取计划
					EObject schedule = this.api.getByOid(scheduleOid);
					if (schedule == null) {
						return "没有找到计划!";
					}

					// 获取所属项目
					String projectOid = this.api.getAttributeValue(schedule, "projectId");
					if (StringUtils.isEmpty(projectOid)) {
						return "计划没有设定项目!";
					}
					EObject project = this.api.getByOid(projectOid);
					if (project == null) {
						return "计划选择的所属项目不正确，请先选择所属项目再导入计划。";
					}

					ConfigType taskConfigType = this.api.getConfigTypeByKey(ScheduleTask.TYPE_SCHEDULE_TASK);
					ConfigStatus workConfigStatus = this.api.getStatusByKey(ConfigStatus.STATUS_KEY_WORKING);

					// 读取并验证表格内容
					List<TaskInfo> taskList = readAndValidExcel2(originalFilename, inputStream);

					// 查询项目成员
					List<TeamMember> teamMemberList = teamMemberService.getAllProjectTeamMember(project);

					Map<String, String> deptAndUserMap = new HashMap<String, String>();

					StringBuilder validErr = new StringBuilder();

					// 验证每个责任人都是项目成员
					// 读出所有责任人
					List<String> allOwnerUser = new ArrayList<String>();
					for (TaskInfo taskInfo : taskList) {
						String ownerUserId = taskInfo.getOwnerUserId();
						if (allOwnerUser.contains(ownerUserId)) {
							continue;
						}
						allOwnerUser.add(ownerUserId);
						logger.info(StringUtils.format("涉及到责任人:{}", ownerUserId));
					}
					validOwnerUser2(allOwnerUser, teamMemberList, validErr);

					if (StringUtils.isNotEmpty(validErr.toString())) {
						return StringUtils.format("导入计划的责任人在当前项目成员中不存在，请检查:<br>{}", validErr.toString());
					}

					// 导入任务
					logger.info(StringUtils.format("共计[{}]个任务需要导入", taskList.size()));
					int i = 0;
					boolean hasErr = false;
					StringBuilder errMsg = new StringBuilder();

					ConfigAttribute phaseConfig = attributeManager.get(taskConfigType.getOid(), ScheduleTask.ATTR_PHASE);
					ConfigAttribute deptConfig = attributeManager.get(taskConfigType.getOid(), ScheduleTask.ATTR_TASK_RESPDEPT);
					ConfigAttribute programConfig = attributeManager.get(taskConfigType.getOid(), "workProgram");
					ConfigAttribute detailConfig = attributeManager.get(taskConfigType.getOid(), "workDetails");

					for (TaskInfo task : taskList) {
						logger.info(StringUtils.format("准备导入任务[{}]:{}", (i + 1), task.toString()));
						try {

							EObject newScheduleTask = importTask2(task, scheduleOid, projectOid, taskConfigType,
									workConfigStatus, phaseConfig, deptConfig, programConfig, detailConfig, deptAndUserMap);
							logger.info(StringUtils.format("创建任务[{}]成功:{}", (i + 1),
									configTypeService.getSimpleName(newScheduleTask)));
						} catch (Exception e) {
							logger.log(Level.SEVERE, GoozaoLoggerUtil.getExceptionMsg(e));
							syslog.error("{}", e);
							hasErr = true;
							errMsg.append("创建任务[" + (i + 1) + "]失败:").append(e.getMessage()).append("<br>");
						}
						i++;
					}

					if (hasErr) {
						return StringUtils.format("导入存在失败<br>{}", errMsg.toString());
					}

					return "导入成功";
				} catch (Exception e) {
					logger.log(Level.SEVERE, GoozaoLoggerUtil.getExceptionMsg(e));
					syslog.error("", e);
					return GoozaoLoggerUtil.getExceptionMsg(e);
				} finally {
					GoozaoLoggerUtil.closeLog(logger);
				}
	}


	/**
	 * 验证每个责任人都是项目成员
	 */
	private void validOwnerUser2(List<String> allOwnerUser, List<TeamMember> teamMemberList, StringBuilder validErr) {
		for (String targetOwnerUser : allOwnerUser) {
			logger.info(StringUtils.format("验证责任人:{}", targetOwnerUser));
			boolean findout = false;
			for (TeamMember tm : teamMemberList) {
				String userId = tm.getUserId();
				if (targetOwnerUser.equals(userId)) {
					findout = true;
					break;
				}
			}

			if (!findout) {
				validErr.append(targetOwnerUser).append("<br>");
			}
		}
	}

	private EObject importTask2(TaskInfo task, String scheduleOid, String projectOid, ConfigType taskConfigType,
			ConfigStatus workConfigStatus, ConfigAttribute phaseConfig, ConfigAttribute deptConfig,
			ConfigAttribute programConfig, ConfigAttribute detailConfig, Map<String, String> deptAndUserMap)
			throws Exception {
		if (StringUtils.isEmpty(task.getCode())) {
			EObject scheduleTask = new EObject();
			String newOid = UUID.randomUUID().toString();
			this.api.setAttributeValue(scheduleTask, GModel.ATTR_OID, newOid);
			this.api.setAttributeValue(scheduleTask, GModel.ATTR_OTYPE, taskConfigType.getOid());
			this.api.setAttributeValue(scheduleTask, GModel.ATTR_ONAME, task.getTaskName());
			this.api.setAttributeValue(scheduleTask, GModel.ATTR_REV_ID,
					configTypeService.nextRev(taskConfigType.getOid(), null));
			this.api.setAttributeValue(scheduleTask, GModel.ATTR_OSTATUS, workConfigStatus.getOid());
			this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_OWNER_PROJECT, projectOid);
			this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_SCHEDULE, scheduleOid);

			this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_TASK_IS_RELEASED, "N"); // 计划已发布
			this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_TASK_STATUS, ScheduleTask.TASK_STATUS_UNDEFINED); // 任务状态

			String phaseValue = dictManager.getValue(phaseConfig.getDict(), task.getPhase());
			this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_PHASE, phaseValue); // 项目阶段

			String workProgram = dictManager.getValue(programConfig.getDict(), task.getWorkProgram());
			this.api.setAttributeValue(scheduleTask, "workProgram", workProgram); // 工作程序

			String workDetails = dictManager.getValue(detailConfig.getDict(), task.getWorkDetail());
			this.api.setAttributeValue(scheduleTask, "workDetails", workDetails); // 工作明细

			String targetDespDept = task.getRespDept();
			String deptValue = dictManager.getValue(deptConfig.getDict(), targetDespDept);
			this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_TASK_RESPDEPT, deptValue); // 责任部门
			
			this.api.setAttributeValue(scheduleTask, GModel.ATTR_OWNERUSER, task.getOwnerUserId()); // 责任人

			if ("项目质量监督".equals(targetDespDept)) {
				this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_IS_MILESTONE, "Y"); // 里程碑
			}

			this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_PLAN_STARTDATE, task.getPlanStartDate()); // 计划开始时间
			this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_PLAN_ENDDATE, task.getPlanEndDate()); // 计划结束时间
			this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_PROGRESS, "0"); // 已完成%
			this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_DUARATION, task.getWorkDays()); // 持续天数(d)

//			if (!"项目经理".equals(targetDespDept)) {
//				String targetUserId = deptAndUserMap.get(targetDespDept);
//				this.api.setAttributeValue(scheduleTask, GModel.ATTR_OWNERUSER, targetUserId);
//				logger.info(StringUtils.format("任务[{}]的责任部门[{}]，所有者:{}", scheduleTask.getOname(), targetDespDept,
//						targetUserId));
//			} else {
//				logger.info(StringUtils.format("任务[{}]的责任部门[项目经理]，所有者为当前用户，即项目经理", scheduleTask.getOname()));
//			}

			EObject newScheduleTask = this.api.createWithEvent(scheduleTask);
			syslog.info("新建任务:{}", newScheduleTask);
			return newScheduleTask;
		} else {
			// 更新任务
			EObject scheduleTask = this.api.getByTypeAndCodeAndRev(taskConfigType.getOid(), task.getCode(),
					task.getRevId());
			syslog.info("根据编号[{}]、版本号[{}]查询到已有任务:{}", task.getCode(), task.getRevId(), scheduleTask);

			if (scheduleTask != null) {
				String deptValue = dictManager.getValue(deptConfig.getDict(), task.getRespDept());
				this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_TASK_RESPDEPT, deptValue); // 责任部门
				this.api.setAttributeValue(scheduleTask, GModel.ATTR_OWNERUSER, task.getOwnerUserId()); // 责任人
				this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_PLAN_STARTDATE, task.getPlanStartDate()); // 计划开始时间
				this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_PLAN_ENDDATE, task.getPlanEndDate()); // 计划结束时间
				this.api.setAttributeValue(scheduleTask, ScheduleTask.ATTR_DUARATION, task.getWorkDays()); // 持续天数(d)

				this.api.editWithEvent(scheduleTask);
				syslog.info("更新任务:{}", scheduleTask);
			}
			return scheduleTask;
		}
		
	}

	private List<TaskInfo> readAndValidExcel2(String originalFilename, InputStream inputStream) throws IOException {

		Workbook workbook = WorkbookFactory.create(inputStream);
		if (workbook == null) {
			throw new IOException("无法解析到Excel的工作簿!");
		}
		Sheet sheet = workbook.getSheetAt(0);
		if (sheet == null) {
			throw new IOException("无法解析到Excel的第一个Sheet!");
		}
		try {

			// 创建公式求值器
			FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
			StringBuilder err = new StringBuilder(); // 全部校验一遍基本信息
			List<TaskInfo> list = new ArrayList<>();
			int titleNum = 1;
			int rows = sheet.getLastRowNum();
			for (int i = titleNum; i <= rows; i++) {
				Row row = sheet.getRow(i);
				int rowIndex = i + 1;

				int col = 0; // 注意以下读取顺序必须与EXCEL列顺序一致
				String phase = ExcelUtil.getCellValue(row, col++); // 项目阶段
				if (StringUtils.isEmpty(phase)) {
					logger.log(Level.WARNING, "第[" + rowIndex + "]行的项目阶段为空，不再继续读取数据！");
					break;
				}
				String workProgram = ExcelUtil.getCellValue(row, col++); // 工作程序
				String workDetail = ExcelUtil.getCellValue(row, col++); // 工作明细
				String taskCode = ExcelUtil.getCellValue(row, col++); // 任务编号
				String taskRevId = ExcelUtil.getCellValue(row, col++); // 任务版本
				String taskName = ExcelUtil.getCellValue(row, col++); // 任务名称/交付物
				String respDept = ExcelUtil.getCellValue(row, col++); // 责任部门
				String ownerUserId = ExcelUtil.getCellValue(row, col++); // 责任人

				String planStartDate = ""; // 开始时间
				int startCol = col++;
				Cell cell1 = row.getCell(startCol);
				if (cell1 != null && cell1.getCellType() == CellType.FORMULA) {
					CellValue cellValue = evaluator.evaluate(cell1);
					if (CellType.NUMERIC.equals(cellValue.getCellType())) {
						Date date = cell1.getDateCellValue();
						SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
						planStartDate = sdf.format(date);
					}
				}
				if (StringUtils.isEmpty(planStartDate)) {
					planStartDate = ExcelUtil.getCellValue(row, startCol);
				}

				String planEndDate = ""; // 结束时间
				int endCol = col++;
				Cell cell2 = row.getCell(endCol);
				if (cell2 != null && cell2.getCellType() == CellType.FORMULA) {
					CellValue cellValue = evaluator.evaluate(cell2);
					if (CellType.NUMERIC.equals(cellValue.getCellType())) {
						Date date = cell2.getDateCellValue();
						SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
						planEndDate = sdf.format(date);
					}
				}
				if (StringUtils.isEmpty(planEndDate)) {
					planEndDate = ExcelUtil.getCellValue(row, endCol);
				}

				String workDays = ""; // 工作天数
				int dayCol = col++;
				Cell cellDay = row.getCell(dayCol);
				if (cellDay != null && cellDay.getCellType() == CellType.FORMULA) {
					CellValue cellValue = evaluator.evaluate(cellDay);
					if (CellType.NUMERIC.equals(cellValue.getCellType())) {
						workDays = Convert.toInt(cellValue.getNumberValue(), 0) + "";
					}
				}
				if (StringUtils.isEmpty(workDays) || "0".equals(workDays)) {
					workDays = ExcelUtil.getCellValue(row, dayCol);
				}

				TaskInfo task = new TaskInfo();
				task.setCode(taskCode);
				task.setRevId(taskRevId);
				task.setPhase(phase);
				task.setWorkProgram(workProgram);
				task.setWorkDetail(workDetail);
				task.setTaskName(taskName);
				task.setRespDept(respDept);
				task.setOwnerUserId(ownerUserId);
				task.setPlanStartDate(planStartDate);
				task.setPlanEndDate(planEndDate);
				task.setWorkDays(workDays);
				list.add(task);
				logger.info(StringUtils.format("读取第[{}]行:{}", rowIndex, task.toString()));

				if (StringUtils.isEmpty(phase) || StringUtils.isEmpty(taskName) || StringUtils.isEmpty(respDept)
						|| StringUtils.isEmpty(ownerUserId) || StringUtils.isEmpty(planStartDate)
						|| StringUtils.isEmpty(planEndDate) || StringUtils.isEmpty(workDays)) {
					err.append(StringUtils.format("第[{}]行内容有误，项目阶段、任务名称、责任部门、责任人、开始时间、结束时间、工作天数不可为空!<br>", rowIndex));
				}

			}
			if (StringUtils.isNotEmpty(err)) {
				throw new ServiceException("校验失败，请检查数据<br>" + err.toString());
			}
			return list;
		} finally {
			if (workbook != null) {
				workbook.close();
			}
		}
	}

	@Override
	public String exportSchedule2(String scheduleOid) {
		InputStream inputStream = null;

		try {

			// 获取计划
			EObject schedule = this.api.getByOid(scheduleOid);
			if (schedule == null) {
				throw new ServiceException(StringUtils.format("根据[{}]无法找到计划!", scheduleOid));
			}
			String scheduleSimpleName = configTypeService.getSimpleName(schedule);
			syslog.info("选中计划:{}", scheduleSimpleName);

			List<EObject> taskList = scheduleTaskService.getScheduleTasks(schedule);
			if (taskList == null || taskList.isEmpty()) {
				throw new ServiceException(
						StringUtils.format("计划[{}]没有任务，没有要导出的内容!", scheduleSimpleName));
			}

			File file = new File(RuoYiConfig.getTemplate() + TEMPLATE_SCHEDULE_TASK);
			if (file.exists()) {
				syslog.info("file path1:{}", file.getAbsolutePath());
				inputStream = new FileInputStream(file);
			} else {
				syslog.info("file path2:{}", TEMPLATE_SCHEDULE_TASK);
				inputStream = ProjectServiceImpl.class.getResourceAsStream(TEMPLATE_SCHEDULE_TASK);
			}

			if (inputStream == null) {
				throw new ServiceException(
						StringUtils.format("无法获取模版[{}]，请确认模版是否存在或重启服务!", TEMPLATE_SCHEDULE_TASK));
			}
			Workbook workbook = ExcelUtil.createCommonWorkbook(inputStream);
			Sheet sheet = workbook.getSheetAt(0);
			
			// 任务按计划开始时间从前到后排序
			taskList.sort(new Comparator<EObject>() {

				@Override
				public int compare(EObject o1, EObject o2) {
					String startDate1 = api.getAttributeValue(o1, ScheduleTask.ATTR_PLAN_STARTDATE);
					String startDate2 = api.getAttributeValue(o2, ScheduleTask.ATTR_PLAN_STARTDATE);
					return startDate1.compareTo(startDate2);
				}
			});
			
			int startRow = 1;
			for (EObject task : taskList) {
				syslog.info("任务:{}", task);
				String phase = this.api.getAttributeDictLabelValue(task, ScheduleTask.ATTR_PHASE);
				String workProgram = this.api.getAttributeDictLabelValue(task, "workProgram");
				String workDetails = this.api.getAttributeDictLabelValue(task, "workDetails");
				String code = this.api.getAttributeValue(task, GModel.ATTR_CODE);
				String revId = this.api.getAttributeValue(task, GModel.ATTR_REV_ID);
				String oname = this.api.getAttributeValue(task, GModel.ATTR_ONAME);
				String dept = this.api.getAttributeDictLabelValue(task, ScheduleTask.ATTR_TASK_RESPDEPT);
				String ownerUser = this.api.getAttributeValue(task, GModel.ATTR_OWNERUSER);
				String planStartDate = this.api.getAttributeValue(task, ScheduleTask.ATTR_PLAN_STARTDATE);
				String planEndDate = this.api.getAttributeValue(task, ScheduleTask.ATTR_PLAN_ENDDATE);
				String duration = this.api.getAttributeValue(task, ScheduleTask.ATTR_DUARATION);

				ExcelUtil.setValue(sheet, startRow, "A", phase, null); // 项目阶段	
				ExcelUtil.setValue(sheet, startRow, "B", workProgram, null); // 工作程序
				ExcelUtil.setValue(sheet, startRow, "C", workDetails, null); // 工作明细
				ExcelUtil.setValue(sheet, startRow, "D", code, null); // 任务编号	
				ExcelUtil.setValue(sheet, startRow, "E", revId, null); // 任务版本	
				ExcelUtil.setValue(sheet, startRow, "F", oname, null); // 任务名称	
				ExcelUtil.setValue(sheet, startRow, "G", dept, null); // 责任部门	
				ExcelUtil.setValue(sheet, startRow, "H", ownerUser, null); // 责任人
				ExcelUtil.setValue(sheet, startRow, "I", planStartDate, null); // 开始时间	
				ExcelUtil.setValue(sheet, startRow, "J", planEndDate, null); // 结束时间	
				ExcelUtil.setValue(sheet, startRow, "K", duration, null); // 工作天数	
				startRow++;
			}

			String filename = scheduleSimpleName.replace("\\/", "_") + "_任务.xlsx";

			String exportFileFullName = ExcelUtil.generateFileToDownlandDir(workbook, filename);
			if (StringUtils.isNotEmpty(exportFileFullName)) {
				return filename;
			}
		} catch (Exception e) {
			syslog.error("", e);
			throw new ServiceException(e.getMessage());
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					syslog.error("", e);
				}
			}
		}
		return null;
	}

}
